package com.lijianlove.keeplearning.dr.support.aspect;

import com.lijianlove.keeplearning.dr.config.DRConfig;
import com.lijianlove.keeplearning.dr.support.RejectDTO;
import com.lijianlove.keeplearning.dr.support.annotation.DR;
import com.lijianlove.keeplearning.dr.support.builder.KeyBuilder;
import com.lijianlove.keeplearning.dr.support.customize.Processor;
import com.lijianlove.keeplearning.dr.support.log.EventPublisher;
import com.lijianlove.keeplearning.dr.support.log.event.DREvent;
import com.lijianlove.keeplearning.dr.support.log.event.RunLogEvent;
import lombok.SneakyThrows;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.Objects;

/**
 * @author lijian
 * @date 2019/4/30 下午2:40
 */
@Component
@Aspect
public class DRProcessor {

    @Autowired
    private DRConfig drConfig;

    @SneakyThrows
    @Around("@annotation(com.lijianlove.keeplearning.dr.support.annotation.DR)")
    public Object around(ProceedingJoinPoint pjp) {

        MethodSignature signature = (MethodSignature) pjp.getSignature();
        DR dr = signature.getMethod().getAnnotation(DR.class);
        String strClassName = pjp.getTarget().getClass().getName();
        String strMethodName = signature.getName();
        Class returnType = signature.getReturnType();
        Class[] parameterTypes = signature.getParameterTypes();
        String[] parameterNames = signature.getParameterNames();
        // 目前仅支持单参数
        if (Objects.isNull(parameterTypes) || parameterTypes.length > 1) {
            throw new IllegalAccessException();
        }
        Object[] params = pjp.getArgs();
        Class parameterClass = parameterTypes[0];
        Field[] fields = params[0].getClass().getDeclaredFields();

        String key = new KeyBuilder().buildKey(dr, fields, params[0]);

        key = processorBefore(signature, dr, fields, params[0], key);

        if (Objects.nonNull(key)) {

            RunLogEvent event1 = new RunLogEvent();
            event1.setTime(System.currentTimeMillis());
            event1.setEventType(DREvent.EventType.PROCESSOR_STARTING);
            event1.setKeyWord("starting");
            event1.setMsg(String.format("%s starting", key));
            event1.setKey(key);
            EventPublisher.publish(event1);

            RejectDTO rejectDTO = new RejectDTO();

            if (!drConfig.getStorageStrategy().putNX(key, dr.expire())) {
                // 获取 drconfig,执行拒绝策略，失败或者报错都执行拒绝策略
                RunLogEvent event = new RunLogEvent();
                event.setTime(System.currentTimeMillis());
                event.setEventType(DREvent.EventType.PROCESSOR_REJECTED);
                event.setKeyWord("rejected");
                event.setKey(key);
                event.setMsg(String.format("%s rejected", key));
                EventPublisher.publish(event);
                return drConfig.getRejectStrategy().reject(rejectDTO);
            }

            rejectDTO.setKey(key);
            // todo lj 填充 rejectDTO
            try {
                Object object = pjp.proceed();

                RunLogEvent event = new RunLogEvent();
                event.setTime(System.currentTimeMillis());
                event.setEventType(DREvent.EventType.PROCESSOR_FINISHED);
                event.setKeyWord("finished");
                event.setKey(key);
                event.setMsg(String.format("%s finished", key));
                EventPublisher.publish(event);

                return object;
            } catch (Throwable throwable) {
                RunLogEvent event = new RunLogEvent();
                event.setTime(System.currentTimeMillis());
                event.setEventType(DREvent.EventType.PROCESSOR_ERR_REJECTED);
                event.setKeyWord("err_rejected");
                event.setKey(key);
                event.setMsg(String.format("%s error rejected", key));
                EventPublisher.publish(event);

                rejectDTO.setThrowable(throwable);
                // 获取 drconfig,执行拒绝策略，失败或者报错都执行拒绝策略
                return drConfig.getRejectStrategy().reject(rejectDTO);
            } finally {
//            Long endTime = System.currentTimeMillis();
//            log.setTime(endTime - startTime);
//            publisher.publishEvent(new OperationLogEvent(log));
            }
        } else {
            RunLogEvent event = new RunLogEvent();
            event.setTime(System.currentTimeMillis());
            event.setEventType(DREvent.EventType.PROCESSOR_SKIPED);
            event.setKeyWord("skiped");
            event.setKey(key);
            event.setMsg(String.format("%s skiped", key));
            EventPublisher.publish(event);

            return pjp.proceed();
        }
    }

    /**
     * 调用用户自定义的处理前方法
     *
     * @param signature 用户可以根据这个判断方法
     * @param dr
     * @param fields
     * @param param
     * @param key
     */
    private String processorBefore(MethodSignature signature, DR dr, Field[] fields, Object param, String key) throws IllegalAccessException, InstantiationException {
        Class processor = dr.processor();
        Object instance = processor.newInstance();
        if (instance instanceof Processor) {
            Processor customsizeProcessor = (Processor) instance;
            key = customsizeProcessor.processorBefore(signature, dr, fields, param, key);
        }
        return key;
    }
}
